﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using NumericalData.Interpolation.Data;

namespace WPFSolution.UserControls
{
    /// <summary>
    /// Interaction logic for FunctionEvaluator.xaml
    /// </summary>
    public partial class FunctionEvaluator : UserControl
    {
        #region Fields
        private Dictionary<string, ExpressionContainer> _history = new Dictionary<string, ExpressionContainer>();
        private bool _filledFromOutside;
        #endregion

        #region Constructors

        public FunctionEvaluator()
        {
            InitializeComponent();
        }

        #endregion

        #region Dependency Properties

        #region IntervalStart

        public static readonly DependencyProperty IntervalStartProperty =
            DependencyProperty.Register("IntervalStart", typeof(double), typeof(FunctionEvaluator), new PropertyMetadata(default(double)));

        public double IntervalStart
        {
            get { return (double)GetValue(IntervalStartProperty); }
            set { SetValue(IntervalStartProperty, value); }
        }
        #endregion
        #region IntervalStop

        public static readonly DependencyProperty IntervalStopProperty =
            DependencyProperty.Register("IntervalStop", typeof(double), typeof(FunctionEvaluator), new PropertyMetadata(default(double)));

        public double IntervalStop
        {
            get { return (double)GetValue(IntervalStopProperty); }
            set { SetValue(IntervalStopProperty, value); }
        }
        #endregion
        #region PointsNumber

        public static readonly DependencyProperty PointsNumberProperty =
            DependencyProperty.Register("PointsNumber", typeof(int), typeof(FunctionEvaluator), new PropertyMetadata(default(int)));

        public int PointsNumber
        {
            get { return (int)GetValue(PointsNumberProperty); }
            set { SetValue(PointsNumberProperty, value); }
        }
        #endregion
        #region CurrentExpression

        public static readonly DependencyProperty CurrentExpressionProperty =
            DependencyProperty.Register("CurrentExpression", typeof(ExpressionContainer), typeof(FunctionEvaluator), new PropertyMetadata(default(ExpressionContainer)));

        public ExpressionContainer CurrentExpression
        {
            get { return (ExpressionContainer)GetValue(CurrentExpressionProperty); }
            private set { SetValue(CurrentExpressionProperty, value); }
        }
        #endregion
        #region InterpolationOrder

        public static readonly DependencyProperty InterpolationOrderProperty =
            DependencyProperty.Register("InterpolationOrder", typeof(int), typeof(FunctionEvaluator), new PropertyMetadata(default(int)));

        public int InterpolationOrder
        {
            get { return (int)GetValue(InterpolationOrderProperty); }
            set { SetValue(InterpolationOrderProperty, value); }
        }
        #endregion

        #endregion

        #region Delegates

        public delegate void HistoryElementUpdatedGelegate(ExpressionContainer updated);

        public delegate void ExpressionEvaluatedDelegate(ExpressionContainer container);
        #endregion

        #region Inner Events

        public event ExpressionEvaluatedDelegate ExpressionEvaluated;

        public event HistoryElementUpdatedGelegate HistoryUpdated;

        #endregion

        #region History

        private void Historiate(ExpressionContainer exp)
        {
            if (exp.IsValid)
            {
                var found = FindInHistory(exp.GetHashCode().ToString());
                UnmarkPreviousExpression(CurrentExpression);
                if (found == null)
                {
                    CreateNewHistoryItem(exp);
                    CurrentExpression = exp;
                }
                else
                {
                    MarkExpression(found);
                    CurrentExpression = found;
                    HistoryUpdated(found);
                }
            }
        }

        private ExpressionContainer FindInHistory(string key)
        {
            var found = (from container in _history
                         where container.Key == key
                         select container.Value).FirstOrDefault<ExpressionContainer>();
            return found;
        }

        private void CreateNewHistoryItem(ExpressionContainer exp)
        {
            var key = exp.GetHashCode().ToString();
            _history.Add(key, exp);
            var historyItem =
            new ListBoxItem
            {
                Content =String.Format("{0} from: {1}, to: {2}, points: {3}, order:{4}", 
                        exp.Expression,exp.IntervalStart, 
                        exp.IntervalStop, exp.Grid.Count,exp.MethodOrder),
                Tag = key,
                FontWeight = FontWeights.Bold
            };
            historyItem.MouseDoubleClick += historyItem_MouseDown;
            historyContainer.Items.Add(historyItem);
        }

        private void UnmarkPreviousExpression(ExpressionContainer prev)
        {
            if (prev != null)
            {
                var prevItem = FindItemInHistoryBox(prev.GetHashCode().ToString());
                if (prevItem == null)
                    return;
                prevItem.FontWeight = FontWeights.Normal;
            }
        }

        private void MarkExpression(ExpressionContainer exp)
        {
            if (exp != null)
            {
                var item = FindItemInHistoryBox(exp.GetHashCode().ToString());
                if (item == null)
                    return;
                item.FontWeight = FontWeights.Bold;
            }
        }

        private ListBoxItem FindItemInHistoryBox(string key)
        {
            foreach (ListBoxItem item in historyContainer.Items)
            {
                if (item.Tag.ToString() == key)
                    return item;
            }
            return null;
        }

        private void LoadExpressionInfo(ExpressionContainer exp)
        {
            resultTextBox.FontWeight = FontWeights.Normal;
            expressionTextBox.Text = exp.Expression;
            resultTextBox.Text = String.Format("Current Expression {0}\nInterval start: {1}" +
                                               "\nInterval stop: {2}\nNumber Of Points: {3}"+
                                               "\nMethod Order: {4}",
                exp.Expression, exp.IntervalStart, exp.IntervalStop, exp.Grid.Count,exp.MethodOrder);
            CurrentExpression = exp;
        }
        #endregion

        #region History Events

        private void historyItem_MouseDown(object sender, MouseButtonEventArgs e)
        {
            var item = (ListBoxItem)sender;
            var key = item.Tag.ToString();
            var expr = FindInHistory(key);
            Historiate(expr);
            LoadExpressionInfo(expr);
        }

        private void expressionTextBox_KeyDown(object sender, KeyEventArgs e)
        {
            if (e.Key == Key.Enter)
            {
                OperateExpression();
            }
        }

        #endregion

        #region Expressions Operations

        private void OperateExpression()
        {
            ExpressionContainer expression;
            if(!TryCreateExpressionFromText(out expression) && !TryLoadFromTextBox(ref expression))
            {
                resultTextBox.FontWeight = FontWeights.UltraBold;
                resultTextBox.Text =
                    String.Format("Expression: {0}\nStatus: {1}",
                    expression.Expression, expression.Status);
            }else
            {
                Historiate(expression);
                LoadExpressionInfo(expression);
            }
        }

        private bool TryLoadFromTextBox(ref ExpressionContainer expression)
        {
            var text = expressionTextBox.Text;
            foreach (KeyValuePair<string, ExpressionContainer> expressionContainer in _history)
            {
                if (expressionContainer.Value.Expression == text)
                {
                    expression = expressionContainer.Value;
                    return expression.IsValid;
                }
            }
            return expression.IsValid;
        }

        private bool TryCreateExpressionFromText(out ExpressionContainer exp)
        {
            var newExp =  new ExpressionContainer(
                    expressionTextBox.Text,
                    "x",
                    IntervalStart,
                    IntervalStop,
                    PointsNumber,
                    InterpolationOrder);
            exp = newExp;
            return exp.IsValid;
        }

        public bool TryLoadExpressionFromGrid(EquidistantPointsGrid grid, string text, int methodOrder)
        {
            var start = grid.Values[0].X;
            var stop = grid.Values[grid.Count - 1].X;
            ExpressionContainer container =
                new ExpressionContainer(text, "none", start, stop, grid, methodOrder)
                    {
                        LoadedFromFile = true
                    };
            if (container.IsValid)
            {
                Historiate(container);
                LoadExpressionInfo(container);
                OperateExpression();
                return true;
            }
            return false;
        }

        #endregion

        private void BuildGraphButton_Click(object sender, RoutedEventArgs e)
        {
            OperateExpression();
            if (CurrentExpression != null)
            {
                if (ExpressionEvaluated != null)
                    ExpressionEvaluated(CurrentExpression);
            }
        }
    }
}
